using System;
using System.Collections.ObjectModel;
using System.Xml;
using Daenet.Syndication;

namespace Daenet.Syndication
{
    /// <summary>
    /// 
    /// </summary>
    public class SyndicationItem
    {
        #region Member Fields

        private string m_Source;

        private string m_Title;

        private string m_Id;

        private DateTime m_LastUpdatedTime;

        private DateTime m_PublishDate;

        private string m_Summary;

        private SyndicationContent m_Content;

        private string m_Copyright;

        private SyndicationFeed m_SourceFeed;

        private Collection<SyndicationPerson> m_Contributors = new Collection<SyndicationPerson>();

        private Collection<SyndicationLink> m_Links = new Collection<SyndicationLink>();

        private Collection<SyndicationPerson> m_Authors = new Collection<SyndicationPerson>();

        private Collection<SyndicationCategory> m_Categories = new Collection<SyndicationCategory>();

        #endregion

        #region Member Properties

        /// <summary>
        /// <c>Title</c> property returns a <c>String</c> construct that conveys a human readable title for the <c>SyndicationItem</c> 
        /// </summary>
        public string Title
        {
            get { return m_Title; }
            set { m_Title = value; }
        }

        /// <summary>
        /// <c>Authors</c> property returns a <c>SyndicationPerson</c> collection that contains the authors of the <c>SyndicationItem</c>
        /// </summary>
        /// <seealso cref="SyndicationPerson"/>
        public Collection<SyndicationPerson> Authors
        {
            get { return m_Authors; }
            set { m_Authors = value; }
        }

        /// <summary>
        /// <c>BaseUri</c> property returns the source uri of the parent <c>SyndicationItem</c>
        /// </summary>
        public Uri BaseUri
        {
            get { return new Uri(m_Source); }
            set { m_Source = value.AbsolutePath; }
        }

        /// <summary>
        /// <c>Categories</c> property returns the category attribute entries of the <c>SyndicationItem</c>
        /// </summary>
        /// <seealso cref="SyndicationCategory"/>
        public Collection<SyndicationCategory> Categories
        {
            get { return m_Categories; }
            set { m_Categories = value; }
        }

        /// <summary>
        /// <c>Content</c> property returns the content attribute of a feed item/entry 
        /// </summary>
        /// <seealso cref="SyndicationContent"/>
        public SyndicationContent Content
        {
            get { return m_Content; }
            set { m_Content = value; }
        }

        /// <summary>
        /// <c>Id</c> property returns the Id attribute of a feed item / entry element which conveys a permanent, universally unique identifier for an entry or feed.
        /// </summary>
        public string Id
        {
            get { return m_Id; }
            set { m_Id = value; }
        }

        /// <summary>
        /// <c>LastUpdateTime</c> property returns the lastupdate attribute of the feed item.
        /// </summary>
        public DateTime LastUpdatedTime
        {
            get { return m_LastUpdatedTime; }
            set { m_LastUpdatedTime = value; }
        }

        /// <summary>
        /// <c>Links</c> property returns a collection of <c>SydicationLink</c> instance which are associated with the current instance of the <c>SyndicationItem</c>
        /// </summary>
        /// <seealso cref="SyndicationLink"/>
        public Collection<SyndicationLink> Links
        {
            get { return m_Links; }
            set { m_Links = value; }
        }

        /// <summary>
        /// <c>PublishDate</c> property returns the date the syndication feed was published.
        /// </summary>
        public DateTime PublishDate
        {
            get { return m_PublishDate; }
            set { m_PublishDate = value; }
        }

        /// <summary>
        /// <c>Summary</c> property returns the summary attribute of the <c>SyndicationItem</c>
        /// </summary>
        public string Summary
        {
            get { return m_Summary; }
            set { m_Summary = value; }
        }

        /// <summary>
        /// Returns a collection of <c>SyndicationPerson</c> which are the contributors of the <c>SyndicationItem</c>
        /// </summary>
        /// <seealso cref="SyndicationPerson"/>
        public Collection<SyndicationPerson> Contributors
        {
            get { return m_Contributors; }
            set { m_Contributors = value; }
        }

        /// <summary>
        /// Returns the copyright text of the <c>SyndicationItem</c>
        /// </summary>
        public string Copyright
        {
            get { return m_Copyright; }
            set { m_Copyright = value; }
        }

        /// <summary>
        /// Returns the orignal source <c>SyndicationFeed</c> that the current <c>SyndicationItem</c> was taken from.
        /// </summary>
        public SyndicationFeed SourceFeed
        {
            get { return m_SourceFeed; }
            set { m_SourceFeed = value; }
        }

        #endregion

        #region Member Methods

        /// <summary>
        /// Public Constructor (default)
        /// </summary>
        public SyndicationItem()
        {
        }

        /// <summary>
        /// Public Constructor (overload)
        /// </summary>
        /// <param name="source">Source Item Object</param>
        protected SyndicationItem(SyndicationItem source)
        {
            m_Source = source.BaseUri.AbsolutePath;
            Authors = source.Authors;
            Categories = source.Categories;
            Content = source.Content;
            Id = source.Id;
            LastUpdatedTime = source.LastUpdatedTime;
            Links = source.Links;
            PublishDate = source.PublishDate;
        }

        /// <summary>
        /// Public Constructor (overload)
        /// </summary>
        /// <param name="title">Title of the item</param>
        /// <param name="content">Content element of the item</param>
        /// <param name="itemAlternateLink"></param>
        public SyndicationItem(string title, string content, Uri itemAlternateLink)
        {
            m_Title = title;
            m_Content = new SyndicationContent();
            m_Content.Value = content;
        }

        /// <summary>
        /// Public Constructor (overload)
        /// </summary>
        /// <param name="title">Title of the item</param>
        /// <param name="content">Content element of the item</param>
        /// <param name="itemAlternateLink"></param>
        /// <param name="id">Id to be used for specific item</param>
        /// <param name="lastUpdatedTime">Last update time</param>
        public SyndicationItem(string title, string content, Uri itemAlternateLink, string id, string lastUpdatedTime)
        {

        }

        /// <summary>
        /// Public Constructor (overload)
        /// </summary>
        /// <param name="title">Title of the item</param>
        /// <param name="content">Content element of the item</param>
        /// <param name="itemAlternateLink"></param>
        /// <param name="id">Id to be used for specific item</param>
        /// <param name="lastUpdatedTime">Last update time</param>
        public SyndicationItem(string title, SyndicationContent content, Uri itemAlternateLink, string id, DateTime lastUpdatedTime)
        {

        }

        /// <summary>
        /// Static Method that parses the Item Construct
        /// </summary>
        /// <param name="reader">XmlReader instance that holds the XML data</param>
        /// <param name="version">Version of the Feed</param>
        /// <returns></returns>
        internal static SyndicationItem TryParse(XmlReader reader, SyndicationVersion version)
        {
            SyndicationItem item = new SyndicationItem();

            #region Atom 1.0
            if (version == SyndicationVersion.Atom10)
            {
                while (true)
                {
                    reader.Read();

                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            #region Author & Contributor
                            case "author":
                                item.Authors.Add(SyndicationPerson.TryParse(reader));
                                break;
                            case "contributor":
                                item.Contributors.Add(SyndicationPerson.TryParse(reader));
                                break;
                            #endregion

                            #region Category
                            case "category":
                                SyndicationCategory category = new SyndicationCategory();
                                category.Name = reader.GetAttribute("term") ?? string.Empty;
                                category.Scheme = reader.GetAttribute("scheme") ?? string.Empty;
                                item.Categories.Add(category);
                                break;
                            #endregion

                            #region Content
                            case "content":
                                SyndicationContent content = new SyndicationContent();

                                reader.Read();

                                content.Value = reader.Value;
                                content.Type = reader.GetAttribute("type") ?? String.Empty;
                                item.Content = content;
                                break;
                            #endregion

                            #region Id
                            case "id":
                                reader.Read();
                                item.Id = reader.Value;
                                break;
                            #endregion

                            #region Link
                            case "link":
                                //reader.Read();
                                //string url = reader.GetAttribute("href") ?? string.Empty;
                                //SyndicationLink link = new SyndicationLink(new Uri(url, UriKind.RelativeOrAbsolute));
                                //if (reader.AttributeCount > 1)
                                //{
                                //    link.RelationshipType = reader.GetAttribute("rel") ?? string.Empty;
                                //    link.MediaType = reader.GetAttribute("type") ?? string.Empty;
                                //    link.Title = reader.GetAttribute("title") ?? string.Empty;
                                //    //long length;
                                //    //long.TryParse(reader.GetAttribute("length"), out length);
                                //    link.Length = reader.GetAttribute("length");
                                //}
                                //item.Links.Add(link);
                                item.Links.Add(SyndicationLink.TryParse(reader));
                                break;
                            #endregion

                            #region Publish Date
                            case "published":
                                reader.Read();
                                item.PublishDate = XmlConvert.ToDateTime(reader.Value, XmlDateTimeSerializationMode.RoundtripKind);
                                //item.PublishDate = reader.Value;
                                break;
                            #endregion

                            #region Update Date
                            case "updated":
                                reader.Read();
                                item.LastUpdatedTime = XmlConvert.ToDateTime(reader.Value, XmlDateTimeSerializationMode.RoundtripKind);
                                break;
                            #endregion

                            #region Source
                            case "source": // TODO
                                item.SourceFeed = SyndicationFeed.TryParse(reader, SyndicationVersion.Atom10);
                                break;
                            #endregion

                            #region Summary
                            case "summary":
                                reader.Read();
                                item.Summary = reader.Value;
                                break;
                            #endregion

                            #region Title
                            case "title":
                                reader.Read();
                                item.Title = reader.Value;
                                break;
                            #endregion

                            #region Copyright
                            case "rights":
                                reader.Read();
                                item.Copyright = reader.Value;
                                break;
                            #endregion
                        }
                    }

                    if (reader.Name == "entry" && (reader.NodeType == XmlNodeType.EndElement || reader.NodeType == XmlNodeType.EndElement))
                        break;
                }

            }
            #endregion

            #region RSS 2.0
            else if (version == SyndicationVersion.RSS20)
            {
                while (true)
                {
                    reader.Read();
                    if ((reader.Name == "author" ||
                        (reader.LocalName == "creator" && reader.NamespaceURI == "http://purl.org/dc/elements/1.1/"))
                        && reader.NodeType == XmlNodeType.Element)
                    {
                        SyndicationPerson person = new SyndicationPerson();
                        item.Authors.Add(person);
                        reader.Read();
                        person.Name = reader.Value;
                    }
                    else if (reader.Name == "title" && reader.NodeType == XmlNodeType.Element)
                    {
                        reader.Read();
                        item.Title = reader.Value;
                    }
                    else if (reader.Name == "description" && reader.NodeType == XmlNodeType.Element)
                    {
                        reader.Read();
                        SyndicationContent content = new SyndicationContent();
                        content.Value = reader.Value;
                        item.Content = content;
                    }

                    else if (reader.Name == "category" && reader.NodeType == XmlNodeType.Element)
                    {
                        SyndicationCategory cat = new SyndicationCategory();
                        reader.Read();
                        cat.Name = reader.Value;
                        item.Categories.Add(cat);
                    }

                    else if (reader.Name == "guid" && reader.NodeType == XmlNodeType.Element)
                    {
                        reader.Read();
                        item.Id = reader.Value;
                    }

                    else if (reader.Name == "pubDate" && reader.NodeType == XmlNodeType.Element)
                    {
                        reader.Read();
                        DateTime publish;
                        try
                        {
                            publish = Convert.ToDateTime(reader.Value);
                        }
                        catch(FormatException)
                        {
                            publish = DateTime.MinValue;
                        }
                        item.PublishDate = publish;
                    }

                    else if (reader.LocalName == "link" && reader.NodeType == XmlNodeType.Element)
                    {
                        reader.Read();
                        string url = reader.Value;
                        if (string.IsNullOrEmpty(url) == false)
                        {
                            SyndicationLink link = new SyndicationLink(new Uri(url));
                            item.Links.Add(link);
                        }
                    }

                    if (reader.Name == "item" && reader.NodeType == XmlNodeType.EndElement)
                        break;
                }

            }
            #endregion

            return item;
        }
        #endregion
    }

}
